package com.alax.interfaces.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import com.alax.common.core.domain.AjaxResult;
import com.alax.common.core.redis.RedisCache;
import com.alax.interfaces.domain.ApiCallRequest;
import com.alax.interfaces.domain.ApiInfo;
import com.alax.interfaces.domain.ApiCallLog;
import com.alax.interfaces.domain.UserApiKey;
import com.alax.interfaces.domain.ApiCallStats;
import com.alax.interfaces.mapper.ApiInfoMapper;
import com.alax.interfaces.mapper.ApiCallLogMapper;
import com.alax.interfaces.mapper.UserApiKeyMapper;
import com.alax.interfaces.mapper.ApiCallStatsMapper;
import com.alax.interfaces.service.IApiCallService;
import com.alax.interfaces.utils.SignatureUtils;
import com.alax.common.utils.ip.IpUtils;
import com.alax.common.utils.ServletUtils;
import cn.hutool.core.util.StrUtil;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import cn.hutool.http.ContentType;
import java.util.List;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;

@Service
public class ApiCallServiceImpl implements IApiCallService {
    
    private static final Logger log = LoggerFactory.getLogger(ApiCallServiceImpl.class);
    
    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private ApiInfoMapper apiInfoMapper;
    
    @Autowired
    private ApiCallLogMapper apiCallLogMapper;
    
    @Autowired
    private UserApiKeyMapper userApiKeyMapper;
    
    @Autowired
    private ApiCallStatsMapper apiCallStatsMapper;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final long TIMESTAMP_VALID_WINDOW = 5 * 60 * 1000; // 5分钟有效期
    
    @Override
    @Transactional
    public AjaxResult callApi(ApiCallRequest request) {
        // 1. 获取API信息
        ApiInfo apiInfo = apiInfoMapper.selectApiInfoById(request.getApiId());
        if (apiInfo == null) {
            return AjaxResult.error("API不存在");
        }
        
        // 2. 检查API状态
        if (apiInfo.getStatus() != 1) {
            return AjaxResult.error("API未上线或维护中");
        }
        
        // 3. 检查认证要求
        if (apiInfo.getAuthRequired() == 1 && !checkApiPermission(request.getApiId(), request.getUserId())) {
            return AjaxResult.error("无调用权限");
        }
        
        // 4. 检查调用限制
        if (!checkCallLimit(request.getApiId(), request.getUserId())) {
            return AjaxResult.error("超出每日调用限制");
        }
        
        // 5. 检查QPS限制
        if (!checkQpsLimit(request.getApiId())) {
            return AjaxResult.error("超出QPS限制");
        }
        
        try {
            // 6. 记录调用开始时间
            long startTime = System.currentTimeMillis();
            
            // 7. 执行API调用（这里需要根据实际情况实现）
            Object result = executeApiCall(apiInfo, request.getParams());
            
            // 8. 计算响应时间
            long responseTime = System.currentTimeMillis() - startTime;
            
            // 9. 异步记录调用日志
            saveCallLog(request, apiInfo, true, null, responseTime);
            
            // 10. 更新调用统计（使用Redis递增）
            updateCallStats(request.getApiId(), request.getUserId());
            
            return AjaxResult.success(result);
        } catch (Exception e) {
            // 记录失败日志
            saveCallLog(request, apiInfo, false, e.getMessage(), 0);
            return AjaxResult.error(e.getMessage());
        }
    }
    
    @Override
    public boolean checkApiPermission(Long apiId, Long userId) {
        try {
            // 1. 获取用户的API密钥信息
            UserApiKey userApiKey = userApiKeyMapper.selectUserApiKeyById(userId);
            if (userApiKey == null) {
                log.error("用户API密钥不存在, userId: {}", userId);
                return false;
            }
            
            // 2. 检查API密钥状态
            if (userApiKey.getStatus() != 1) {
                log.error("API密钥已禁用, userId: {}", userId);
                return false;
            }
            
            // 3. 检查过期时间
            if (userApiKey.getExpireTime() != null && userApiKey.getExpireTime().before(new Date())) {
                log.error("API密钥已过期, userId: {}", userId);
                return false;
            }
            
            // 4. 验证时间戳（防重放攻击）
            ApiCallRequest request = getRequestFromContext();
            if (request == null || !SignatureUtils.isTimestampValid(request.getTimestamp(), TIMESTAMP_VALID_WINDOW)) {
                log.error("请求时间戳无效, userId: {}", userId);
                return false;
            }
            
            // 5. 验证签名
            Map<String, Object> signParams = new HashMap<>(request.getParams());
            signParams.put("apiId", request.getApiId());
            signParams.put("userId", request.getUserId());
            signParams.put("timestamp", request.getTimestamp());
            signParams.put("nonce", request.getNonce());
            signParams.put("accessKey", request.getAccessKey());
            
            if (!SignatureUtils.verifySignature(signParams, request.getSignature(), userApiKey.getApiSecret())) {
                log.error("签名验证失败, userId: {}", userId);
                return false;
            }
            
            // 6. 验证IP白名单（如果配置了白名单）
            if (StrUtil.isNotBlank(userApiKey.getWhiteIps())) {
                String clientIp = IpUtils.getIpAddr();
                List<String> whiteIps = Arrays.asList(userApiKey.getWhiteIps().split(","));
                if (!whiteIps.contains(clientIp)) {
                    log.error("IP不在白名单中, userId: {}, ip: {}", userId, clientIp);
                    return false;
                }
            }
            
        return true;
        } catch (Exception e) {
            log.error("权限验证异常", e);
            return false;
        }
    }
    
    @Override
    public boolean checkCallLimit(Long apiId, Long userId) {
        try {
            // 获取API的每日调用限制
            ApiInfo apiInfo = apiInfoMapper.selectApiInfoById(apiId);
            if (apiInfo == null) {
                log.error("API不存在, apiId: {}", apiId);
                return false;
            }
            
            // 使用带版本前缀的键，避免与现有数据冲突
            String key = "v2:api:call:daily:" + apiId + ":" + userId;
            
            // 获取当前调用次数
            Long currentCount = 0L;
            try {
                Object value = redisTemplate.opsForValue().get(key);
                if (value != null) {
                    // 尝试转换为Long
                    if (value instanceof Long) {
                        currentCount = (Long) value;
                    } else if (value instanceof Integer) {
                        currentCount = ((Integer) value).longValue();
                    } else if (value instanceof String) {
                        currentCount = Long.parseLong((String) value);
                    }
                }
                log.debug("获取当前调用次数, key: {}, count: {}", key, currentCount);
            } catch (Exception e) {
                // 如果获取或转换失败，重置计数
                log.warn("获取调用次数失败，重置计数, key: {}", key, e);
                redisTemplate.delete(key);
                currentCount = 0L;
            }
            
            // 检查是否超过限制
            boolean result = currentCount < apiInfo.getDailyLimit();
            if (!result) {
                log.warn("超出每日调用限制, apiId: {}, userId: {}, currentCount: {}, limit: {}", 
                    apiId, userId, currentCount, apiInfo.getDailyLimit());
            }
            return result;
        } catch (Exception e) {
            log.error("检查调用限制时发生错误, apiId: {}, userId: {}", apiId, userId, e);
            return false;
        }
    }
    
    @Override
    public boolean checkQpsLimit(Long apiId) {
        try {
            // 获取API的QPS限制
            ApiInfo apiInfo = apiInfoMapper.selectApiInfoById(apiId);
            if (apiInfo == null) {
                log.error("API不存在, apiId: {}", apiId);
                return false;
            }
            
            // 使用带版本前缀的键，避免与现有数据冲突
            String key = "v2:api:call:qps:" + apiId + ":" + (System.currentTimeMillis() / 1000);
            
            // 获取当前QPS计数
            Long currentCount = 0L;
            try {
                Object value = redisTemplate.opsForValue().get(key);
                if (value != null) {
                    // 尝试转换为Long
                    if (value instanceof Long) {
                        currentCount = (Long) value;
                    } else if (value instanceof Integer) {
                        currentCount = ((Integer) value).longValue();
                    } else if (value instanceof String) {
                        currentCount = Long.parseLong((String) value);
                    }
                }
                log.debug("获取当前QPS计数, key: {}, count: {}", key, currentCount);
            } catch (Exception e) {
                // 如果获取或转换失败，重置计数
                log.warn("获取QPS计数失败，重置计数, key: {}", key, e);
                redisTemplate.delete(key);
                currentCount = 0L;
            }
            
            // 检查是否超过限制
            boolean result = currentCount < apiInfo.getQpsLimit();
            if (!result) {
                log.warn("超出QPS限制, apiId: {}, currentCount: {}, limit: {}", 
                    apiId, currentCount, apiInfo.getQpsLimit());
            }
            return result;
        } catch (Exception e) {
            log.error("检查QPS限制时发生错误, apiId: {}", apiId, e);
            return false;
        }
    }
    
    /**
     * 执行实际的API调用
     */
    private Object executeApiCall(ApiInfo apiInfo, Map<String, Object> params) {
        try {
            // 1. 验证和处理URL
            String url = apiInfo.getUrl();
            if (url == null || url.trim().isEmpty()) {
                throw new RuntimeException("API URL不能为空");
            }
            
            // 移除URL中的多余空格
            url = url.trim();
            
            // 如果URL不是以http或https开头，则添加http://
            if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) {
                url = "http://" + url;
            }
            
            // 验证URL格式
            try {
                new java.net.URL(url);
            } catch (Exception e) {
                throw new RuntimeException("无效的API URL: " + url);
            }
            
            // 2. 获取请求方法
            String methodStr = apiInfo.getMethod();
            if (methodStr == null || methodStr.trim().isEmpty()) {
                methodStr = "GET"; // 默认使用GET方法
            }
            Method method;
            try {
                method = Method.valueOf(methodStr.toUpperCase());
            } catch (IllegalArgumentException e) {
                throw new RuntimeException("不支持的HTTP方法: " + methodStr);
            }
            
            // 3. 创建请求对象
            HttpRequest request = HttpRequest.of(url).method(method);
            
            // 4. 设置通用请求头
            request.header("Content-Type", ContentType.JSON.getValue());
            request.header("Accept", ContentType.JSON.getValue());
            request.header("User-Agent", "Alax-API-Platform");
            request.timeout(10000); // 设置10秒超时
            
            // 5. 处理请求参数
            String response;
            if (method == Method.GET) {
                // GET请求：将参数添加到URL
                if (params != null && !params.isEmpty()) {
                    request.form(params);
                }
                response = request.execute().body();
            } else {
                // POST/PUT/DELETE请求：将参数作为JSON发送
                if (params != null && !params.isEmpty()) {
                    request.body(objectMapper.writeValueAsString(params));
                }
                response = request.execute().body();
            }
            
            // 6. 处理响应
            if (StrUtil.isBlank(response)) {
                return null;
            }
            
            // 7. 尝试解析JSON响应
            try {
                return objectMapper.readValue(response, Object.class);
            } catch (Exception e) {
                // 如果不是JSON，直接返回字符串
                return response;
            }
            
        } catch (Exception e) {
            log.error("API调用失败: {}", e.getMessage());
            throw new RuntimeException("API调用失败: " + e.getMessage());
        }
    }
    
    /**
     * 保存调用日志
     */
    private void saveCallLog(ApiCallRequest request, ApiInfo apiInfo, boolean success, String errorMsg, long responseTime) {
        try {
            log.info("开始保存API调用日志, apiId: {}, userId: {}", request.getApiId(), request.getUserId());
            
            ApiCallLog apiCallLog = new ApiCallLog();
            // 基本信息
            apiCallLog.setApiId(request.getApiId());
            apiCallLog.setUserId(request.getUserId());
            
            // 获取用户的API密钥信息
            UserApiKey userApiKey = userApiKeyMapper.selectUserApiKeyById(request.getUserId());
            final String apiKey = (userApiKey != null) ? userApiKey.getApiKey() : "unknown";
            if (userApiKey != null) {
                apiCallLog.setApiKey(apiKey);
            } else {
                log.warn("未找到用户的API密钥信息, userId: {}", request.getUserId());
                apiCallLog.setApiKey(apiKey);
            }
            
            // 请求信息
            apiCallLog.setRequestParams(objectMapper.writeValueAsString(request.getParams()));
            apiCallLog.setIp(IpUtils.getIpAddr());
            apiCallLog.setRequestMethod(apiInfo.getMethod());
            apiCallLog.setRequestUrl(apiInfo.getUrl());
            
            // 响应信息
            apiCallLog.setStatus(success ? 1 : 0);
            apiCallLog.setErrorMsg(errorMsg);
            apiCallLog.setResponseTime(responseTime);
            apiCallLog.setCreateTime(new Date());
            
            log.info("准备写入API调用日志: {}", apiCallLog);
            
            // 异步写入日志
            final ApiCallRequest finalRequest = request;
            final boolean finalSuccess = success;
            final long finalResponseTime = responseTime;
            
            CompletableFuture.runAsync(() -> {
                try {
                    int rows = apiCallLogMapper.insertApiCallLog(apiCallLog);
                    log.info("API调用日志写入成功, 影响行数: {}", rows);
                    
                    // 更新API调用统计
                    updateApiCallStats(finalRequest, apiKey, finalSuccess, finalResponseTime);
                } catch (Exception e) {
                    log.error("异步写入调用日志失败: {}", e.getMessage(), e);
                }
            });
        } catch (Exception e) {
            // 记录日志失败不应影响主流程
            log.error("保存API调用日志失败: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 更新API调用统计
     * 
     * @param request API调用请求
     * @param apiKey API密钥
     * @param success 是否调用成功
     * @param responseTime 响应时间
     */
    private void updateApiCallStats(ApiCallRequest request, String apiKey, boolean success, long responseTime) {
        try {
            log.info("开始更新API调用统计, apiId: {}, userId: {}", request.getApiId(), request.getUserId());
            
            // 获取当前日期（仅保留年月日）
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            Date callDate = calendar.getTime();
            
            // 查询是否已有当天的统计记录
            ApiCallStats query = new ApiCallStats();
            query.setApiId(request.getApiId());
            query.setUserId(request.getUserId());
            query.setApiKey(apiKey);
            query.setCallDate(callDate);
            
            List<ApiCallStats> existingStatsList = apiCallStatsMapper.selectApiCallStatsList(query);
            ApiCallStats stats;
            
            if (existingStatsList != null && !existingStatsList.isEmpty()) {
                // 已有记录，更新
                stats = existingStatsList.get(0);
                
                // 更新调用次数
                int totalCalls = stats.getTotalCalls() != null ? stats.getTotalCalls().intValue() + 1 : 1;
                stats.setTotalCalls((long) totalCalls);
                
                if (success) {
                    int successCalls = stats.getSuccessCalls() != null ? stats.getSuccessCalls().intValue() + 1 : 1;
                    stats.setSuccessCalls((long) successCalls);
                } else {
                    int failCalls = stats.getFailCalls() != null ? stats.getFailCalls().intValue() + 1 : 1;
                    stats.setFailCalls((long) failCalls);
                }
                
                // 更新平均响应时间
                int totalCallsForAvg = totalCalls;
                long currentAvgTime = stats.getAvgResponseTime() != null ? stats.getAvgResponseTime() : 0L;
                long newAvgTime;
                
                if (totalCallsForAvg <= 1) {
                    newAvgTime = responseTime;
                } else {
                    long totalTime = currentAvgTime * (totalCallsForAvg - 1) + responseTime;
                    newAvgTime = totalTime / totalCallsForAvg;
                }
                
                stats.setAvgResponseTime(newAvgTime);
                stats.setUpdateTime(new Date());
                
                // 更新记录
                apiCallStatsMapper.updateApiCallStats(stats);
                log.info("API调用统计更新成功, id: {}", stats.getId());
            } else {
                // 没有记录，创建新记录
                stats = new ApiCallStats();
                stats.setApiId(request.getApiId());
                stats.setUserId(request.getUserId());
                stats.setApiKey(apiKey);
                stats.setCallDate(callDate);
                stats.setTotalCalls(1L);
                stats.setSuccessCalls(success ? 1L : 0L);
                stats.setFailCalls(success ? 0L : 1L);
                stats.setAvgResponseTime((long) responseTime);
                stats.setCreateTime(new Date());
                stats.setUpdateTime(new Date());
                
                // 插入新记录
                apiCallStatsMapper.insertApiCallStats(stats);
                log.info("API调用统计创建成功");
            }
        } catch (Exception e) {
            log.error("更新API调用统计失败: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 更新调用统计
     * 
     * @param apiId API ID
     * @param userId 用户ID
     */
    private void updateCallStats(Long apiId, Long userId) {
        try {
            // 更新每日调用次数
            String dailyKey = "v2:api:call:daily:" + apiId + ":" + userId;
            Long dailyCount = 0L;
            try {
                Object value = redisTemplate.opsForValue().get(dailyKey);
                if (value != null) {
                    // 尝试转换为Long
                    if (value instanceof Long) {
                        dailyCount = (Long) value;
                    } else if (value instanceof Integer) {
                        dailyCount = ((Integer) value).longValue();
                    } else if (value instanceof String) {
                        dailyCount = Long.parseLong((String) value);
                    }
                }
            } catch (Exception e) {
                log.warn("获取调用次数失败，重置计数, key: {}", dailyKey, e);
                redisTemplate.delete(dailyKey);
                dailyCount = 0L;
            }
            
            // 增加计数并设置
            dailyCount += 1;
            redisTemplate.opsForValue().set(dailyKey, dailyCount, 24, TimeUnit.HOURS);
            
            // 更新QPS计数
            String qpsKey = "v2:api:call:qps:" + apiId + ":" + (System.currentTimeMillis() / 1000);
            Long qpsCount = 0L;
            try {
                Object value = redisTemplate.opsForValue().get(qpsKey);
                if (value != null) {
                    // 尝试转换为Long
                    if (value instanceof Long) {
                        qpsCount = (Long) value;
                    } else if (value instanceof Integer) {
                        qpsCount = ((Integer) value).longValue();
                    } else if (value instanceof String) {
                        qpsCount = Long.parseLong((String) value);
                    }
                }
            } catch (Exception e) {
                log.warn("获取QPS计数失败，重置计数, key: {}", qpsKey, e);
                redisTemplate.delete(qpsKey);
                qpsCount = 0L;
            }
            
            // 增加计数并设置
            qpsCount += 1;
            redisTemplate.opsForValue().set(qpsKey, qpsCount, 2, TimeUnit.SECONDS);
            
            log.debug("更新调用统计成功, apiId: {}, userId: {}, dailyCount: {}, qpsCount: {}", 
                apiId, userId, dailyCount, qpsCount);
        } catch (Exception e) {
            log.error("更新调用统计失败, apiId: {}, userId: {}", apiId, userId, e);
        }
    }
    
    /**
     * 从当前请求上下文中获取API调用请求
     */
    private ApiCallRequest getRequestFromContext() {
        return (ApiCallRequest) RequestContextHolder.getRequestAttributes()
                .getAttribute("apiCallRequest", RequestAttributes.SCOPE_REQUEST);
    }
}
